import { getOnlyKey } from '../utils'
// 默认数据
const getDefaultData = () => {
	return {
		// 页面背景
		style: {
			width: 320,
			height: 568,
			backgroundColor: '#ffffff',
			backgroundImage: '',
			backgroundPosition: 'center',
			backgroundSize: 'cover',
			backgroundRepeat: 'no-repeat',
			boxSizing: 'content-box',
			left: 0,
			top: 0,
		},
		// 组件渲染列表
		cmps: [],
	}
}

export default class CanvasStore {
	constructor(canvas = getDefaultData()) {
		this._canvas = canvas
		// 当前标记组件索引
		// this.selectedCmpIndex = -1
		// 换为set
		this.assembly = new Set()
		this.listeners = [] // 订阅列表
		this.canvasChangeHistory = [JSON.stringify(this._canvas)] // 存储列表
		this.canvasChangeHistoryIndex = 0 // 当前指针位置
		this.canvasChangeHistoryMax = 100 // 最大存储量
	}
	// 获取画布信息
	getCanvas = () => {
		// 防止污染数据
		return { ...this._canvas }
	}
	// 获取画布下的组件=
	getCanvasCmps = () => {
		return [...this._canvas.cmps]
	}
	// 获取当前选中组件
	getSelectedCmp = () => {
		const cmps = this.getCanvasCmps()
		return cmps[this.getSelectedCmpIndex()]
	}
	// 获取当前选中组件索引
	getSelectedCmpIndex = () => {
		const selectedIndex = Array.from(this.assembly)[0]
		return selectedIndex === undefined ? -1 : selectedIndex
	}
	// 批量选择组件
	addToAssembly = indexs => {
		if (Array.isArray(indexs)) {
			// 因为是全等判断，最好都转为数值
			indexs.forEach(index => index !== -1 && this.assembly.add(index - 0))
		} else {
			indexs !== -1 && this.assembly.add(indexs)
		}
	}
	//组合组件
	hasAssembly = () => {
		return this.assembly.size > 1
	}
	// 批量操作组件
	addAndUpdateAssembly = indexs => {
		this.addToAssembly(indexs)
		this.updateApp()
	}
	// 批量添加（复制）
	addAssemblyCms = () => {
		this.assembly.forEach(index => {
			const cmp = this._canvas.cmps[index]
			const newCmp = JSON.parse(JSON.stringify(cmp))
			newCmp.key = getOnlyKey()
			newCmp.style.top += 20
			newCmp.style.left += 20
			this._canvas.cmps.push(newCmp)
		})
		// 添加组件后,更新选中的组件 例如 原本有 5 7 9 组件 复制了3个 那么就是 5 7 9 10 11 12
		// 那么选中的组件更改为10 11 12
		const cmpsLength = this._canvas.cmps.length
		const assembluSize = this.assembly.size
		this.assembly.clear()
		for (let i = cmpsLength - assembluSize; i < cmpsLength; i++) {
			this.assembly.add(i)
		}
		this.updateApp()
		this.recordCanvasChangeHistory()
	}
	// 判断下标为index的组件是否被批量选中
	belongingToAssembly = index => {
		return this.assembly.has(index)
	}
	// 改变层级 0 1 3 2 4
	// 向上移动 将当前组件顺序+1 顺序,然后将原有的位置替换为选中的数据
	addCmpZIndex = (cmpIndex = this.getSelectedCmpIndex()) => {
		const cmps = this.getCanvasCmps()
		const targetIndex = cmpIndex + 1
		if (targetIndex >= cmps.length) {
			return
		}
		// 变换顺序替换数据,冒泡改变数据
		const tem = cmps[cmpIndex]
		this._canvas.cmps[cmpIndex] = this._canvas.cmps[targetIndex]
		this._canvas.cmps[targetIndex] = tem
		this.selectedCmpIndex = targetIndex
		this.updateApp()
		this.recordCanvasChangeHistory()
	}
	// 向下移动改变数据
	deleCmpZIndex = (cmpIndex = this.getSelectedCmpIndex()) => {
		const cmps = this.getCanvasCmps()
		const targetInde = cmpIndex - 1
		if (targetInde < 0) {
			return
		}
		const tem = cmps[cmpIndex]
		this._canvas.cmps[cmpIndex] = this._canvas.cmps[targetInde]
		this._canvas.cmps[targetInde] = tem
		this.selectedCmpIndex = targetInde
		this.updateApp()
		this.recordCanvasChangeHistory()
	}
	// 设置当前选中组件索引
	setSelectedCmpIndex = newIndex => {
		if (this.getSelectedCmpIndex() === newIndex) return
		// 清空选中列表
		this.assembly.clear()
		if (newIndex > -1) {
			this.addToAssembly(newIndex)
		}
		// 选中后更新组件
		this.updateApp()
	}
	// 置顶
	topZIndex = (cmpIndex = this.getSelectedCmpIndex()) => {
		const cmps = this.getCanvasCmps()
		if (cmpIndex >= cmps.length) {
			return
		}
		// 拼接数组
		this._canvas.cmps = cmps
			.slice(0, cmpIndex)
			.concat(cmps.slice(cmpIndex + 1))
			.concat(cmps[cmpIndex])
		this.addToAssembly(cmps.length - 1)
		this.updateApp()
		this.recordCanvasChangeHistory()
	}
	// 置底
	bottomZIndex = (cmpIndex = this.getSelectedCmpIndex()) => {
		if (cmpIndex <= 0) {
			return
		}
		const cmps = this.getCanvasCmps()
		this._canvas.cmps = [cmps[cmpIndex]]
			.concat(cmps.slice(0, cmpIndex))
			.concat(cmps.slice(cmpIndex + 1))
		this.addToAssembly(0)
		this.updateApp()
		this.recordCanvasChangeHistory()
	}
	// 修改组件属性 style value
	updateSelectedCmp = (newStyle = {}, newvalue) => {
		const selectedCmp = this.getSelectedCmp()
		if (newStyle) {
			selectedCmp.style = { ...selectedCmp.style, ...newStyle }
		}
		if (newvalue !== undefined) {
			selectedCmp.value = newvalue
		}

		this.updateApp()
	}
	// 更新画布信息
	updateCanvasStyle = newStyle => {
		this._canvas.style = {
			...this._canvas.style,
			...newStyle,
		}
		this.updateApp()
		if (newStyle.left === 0) {
			this.recordCanvasChangeHistory()
		}
	}
	// 添加组件
	addCmp = cmp => {
		this._canvas.cmps.push({ ...cmp, key: getOnlyKey() })
		this.assembly.clear()
		this.addToAssembly(this._canvas.cmps.length - 1)
		this.updateApp()
		this.recordCanvasChangeHistory()
	}
	// 删除组件
	deleteCmp = () => {
		// 删除数组 因为选择索引是乱序的 如果从前删除 索引就往前移动，要不然后面索引就是乱的
		// 因此先排序从后往前删除
		const sorted = Array.from(this.assembly).sort((a, b) => b - a)
		sorted.forEach(index => {
			this._canvas.cmps.splice(index, 1)
		})
		this.assembly.clear()
		this.updateApp()
		this.recordCanvasChangeHistory()
	}
	// 更新app
	updateApp = () => {
		this.listeners.forEach(fn => fn())
	}
	subscribe = _fn => {
		this.listeners.push(_fn)
		// 返回取消订阅函数
		return () => {
			this.listeners = this.listeners.filter(fn => fn !== _fn)
		}
	}
	// 设置画布数据
	setCanvas = canvas => {
		if (canvas) {
			Object.assign(this._canvas, canvas)
		} else {
			// 防止引用重复对象修改
			this._canvas = getDefaultData()
		}
		this.updateApp()
		this.recordCanvasChangeHistory()
	}
	// 历史位置
	recordCanvasChangeHistory = () => {
		// 当前指针位置+1存储数据
		this.canvasChangeHistory[++this.canvasChangeHistoryIndex] = JSON.stringify(
			this._canvas,
		)
		// 如果 是在中间进行了新增数据,索引发生了改变，后面的截取不要 1 2 3 5 4
		this.canvasChangeHistory = this.canvasChangeHistory.splice(
			0,
			this.canvasChangeHistoryIndex + 1,
		)
		// 最大100条记录
		if (this.canvasChangeHistory.length > this.canvasChangeHistoryMax) {
			this.canvasChangeHistory.shift()
			this.canvasChangeHistoryIndex--
		}
	}
	//回退历史
	goPrevCanvasHistory = () => {
		let newIndex = this.canvasChangeHistoryIndex - 1
		// 如果到栈底，限制超出
		if (newIndex < 0) {
			newIndex = 0
		}
		// 如果当前index = historyIndex
		if (this.canvasChangeHistoryIndex === newIndex) {
			return
		}
		this.canvasChangeHistoryIndex = newIndex
		const newCanvas = JSON.parse(this.canvasChangeHistory[newIndex])
		this._canvas = newCanvas
		this.updateApp()
	}
	// 上一步
	goNextCanvasHistory = () => {
		let newIndex = this.canvasChangeHistoryIndex + 1
		if (newIndex >= this.canvasChangeHistory.length) {
			newIndex = this.canvasChangeHistory.length - 1
		}
		if (this.canvasChangeHistoryIndex === newIndex) {
			return
		}
		this.canvasChangeHistoryIndex = newIndex
		const newCanvas = JSON.parse(this.canvasChangeHistory[newIndex])
		this._canvas = newCanvas
		this.updateApp()
	}
	// 根据批量选中索引来更新样式 newStyle里面是差值
	updateAssemblyCmps = newStyle => {
		this.assembly.forEach(index => {
			const cmp = this._canvas.cmps[index]
			for (const key in newStyle) {
				cmp.style[key] += newStyle[key] - 0

				if (key === 'transform') {
					// 角度旋转不能 += 要不然会旋转的很大
					cmp.style[key] = newStyle[key]
				}
				if (cmp.style.width < 10) {
					cmp.style.width = 10
				}
				if (cmp.style.height < 10) {
					cmp.style.height = 10
				}
			}
		})
		this.updateApp()
	}
	// 向外暴露的方法
	getPublicCanvas = () => {
		const obj = {
			getCanvas: this.getCanvas,
			getCanvasCmps: this.getCanvasCmps,
			addCmp: this.addCmp,
			subscribe: this.subscribe,
			getSelectedCmpIndex: this.getSelectedCmpIndex,
			setSelectedCmpIndex: this.setSelectedCmpIndex,
			updateSelectedCmp: this.updateSelectedCmp,
			getSelectedCmp: this.getSelectedCmp,
			updateCanvasStyle: this.updateCanvasStyle,
			setCanvas: this.setCanvas,
			recordCanvasChangeHistory: this.recordCanvasChangeHistory,
			goPrevCanvasHistory: this.goPrevCanvasHistory,
			goNextCanvasHistory: this.goNextCanvasHistory,
			deleteCmp: this.deleteCmp,
			updateApp: this.updateApp,
			addCmpZIndex: this.addCmpZIndex,
			deleCmpZIndex: this.deleCmpZIndex,
			topZIndex: this.topZIndex,
			bottomZIndex: this.bottomZIndex,
			addAndUpdateAssembly: this.addAndUpdateAssembly,
			belongingToAssembly: this.belongingToAssembly,
			updateAssemblyCmps: this.updateAssemblyCmps,
			hasAssembly: this.hasAssembly,
			addAssemblyCms: this.addAssemblyCms,
		}
		return obj
	}
}
